package org.example.judge.oj;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.example.judge_common.hander.RetrunMsgException;
import org.example.judge_common.util.CompileUnit;
import org.example.judge_common.util.MyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

@Data
@Slf4j
@AllArgsConstructor
@NoArgsConstructor
public class CompileRun {

    // 源文件资源目录
    private String codePath;


    public static Map<CompileUnit, String> compiles = new HashMap<>();


    /**
     * 将测试数据写入文件
     *
     * @param input
     * @return
     * @throws RetrunMsgException
     */
    public File inputWriter(String input) throws RetrunMsgException {
        File file = new File(this.getCodePath(), "1.in");
        try {
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(input);
            fileWriter.close();
        } catch (IOException e) {
            log.error("输入文件写入失败.msg: {}", e.getMessage());
            throw new RetrunMsgException("服务器繁忙");
        }

        return file;
    }

    /**
     * 将测试数据流写入文件
     *
     * @param input
     * @return
     * @throws RetrunMsgException
     */
    public File inputWriter(InputStream input) throws RetrunMsgException {
        File file = new File(this.getCodePath(), "1.in");
        try {
            // input写入output
            FileOutputStream output = new FileOutputStream(file);
            input.transferTo(output);

            //关闭资源
            input.close();
            output.close();
        } catch (IOException e) {
            log.error("输入文件流写入失败.msg: {}", e.getMessage());
            throw new RetrunMsgException("服务器繁忙");
        }
        return file;
    }

    public Process run(File dataInput, CompileUnit compileUnit) throws RetrunMsgException {

        try {
            switch (compileUnit) {
                case JAVA:
                    return javaRun(dataInput);
                case C:
                    return cRun(dataInput);
                case GCC:
                    return gccRun(dataInput);
                case PYTHON:
                    return pythonRun(dataInput);
                default:
                    log.error("调用未定义编译函数编译运行类型");
                    throw new RetrunMsgException("暂时不支持该编译器运行");
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RetrunMsgException("服务器繁忙");
        }


    }

    private Process pythonRun(File file) throws IOException {
        return Runtime.getRuntime().exec("cmd /c python " + file.getParent()
                + "\\Main.py < " + file.getPath()
                + " > " + file.getParent()+"\\1.out",null,MyUtils.isEmpty(compiles.get(CompileUnit.PYTHON))?null:new File(compiles.get(CompileUnit.PYTHON)));
    }

    private Process gccRun(File file) throws IOException {

        return Runtime.getRuntime().exec("cmd /c " +
                file.getParent() + "\\Main.exe" +
                " < " + file.getPath() + " > " + file.getParent()+"\\1.out");
    }

    private Process cRun(File file) throws IOException {
        return this.gccRun(file);
    }

    private Process javaRun(File inPath) throws IOException {
//        System.out.println("cmd /c java -Xss50M -cp " + inPath.getParent() + " Main < " + inPath.getPath());
        return Runtime.getRuntime().exec("cmd /c java -Xmx512M -cp " + inPath.getParent()
                + " Main < " + inPath.getPath()
                + " > " + inPath.getParent()+"\\1.out",null,MyUtils.isEmpty(compiles.get(CompileUnit.JAVA))?null:new File(compiles.get(CompileUnit.JAVA)));
    }

    public Process compile(String code, CompileUnit compileUnit) throws RetrunMsgException {
        // 将源码写入文件
        File dir = new File(this.getCodePath());
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File file = new File(dir, compileUnit.fileName());
        try {
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(code);
            fileWriter.close();
        } catch (IOException e) {
            log.error("编译的源码写入失败.msg: {}", e.getMessage());
            throw new RetrunMsgException("服务器繁忙");
        }

        // 调用不同编译器
        try {
            switch (compileUnit) {
                case JAVA:
                    return javaCompile(file);
                case GCC:
                    return gccCompile(file);
                case C:
                    return cCompile(file);
                case PYTHON:
                    return pythonCompile(file);
                default:
                    log.error("调用未定义编译函数的编译类型");
                    throw new RetrunMsgException("暂时不支持该编译器");
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RetrunMsgException("服务器繁忙");
        }
    }


    //python不需要编译
    private Process pythonCompile(File path) {
        return null;
    }

    private Process cCompile(File path) throws IOException {

        File ccomplie = MyUtils.isEmpty(this.compiles.get(CompileUnit.C))?new File("g++.exe"):new File(this.compiles.get(CompileUnit.C), "g++.exe");
        log.debug("cmd /c "+ccomplie.getPath() +
                " \"" + path.getPath() + "\"" +
                " -o \"" + path.getParent() + "\\Main.exe\" ");
        return Runtime.getRuntime().exec("cmd /c "+ccomplie.getPath() +
                        " \"" + path.getPath() + "\"" +
                        " -o \"" + path.getParent() + "\\Main.exe\" "
                , null, MyUtils.isEmpty(this.compiles.get(CompileUnit.C))?null:new File(this.compiles.get(CompileUnit.C)));
    }

    private Process gccCompile(File path) throws IOException {
        return this.cCompile(path);
    }

    public Process javaCompile(File filePath) throws IOException {
        return Runtime.getRuntime().exec("cmd /c javac " + filePath.getPath(),null,MyUtils.isEmpty(compiles.get(CompileUnit.JAVA))?null:new File(compiles.get(CompileUnit.JAVA)));
    }

    //编译后的文件是否存在
    public boolean isFile(CompileUnit compileUnit) {
        File file = null;
        switch (compileUnit) {
            case GCC:
            case C:
                file = new File(this.getCodePath(), "Main.exe");
                break;
            case JAVA:
                file = new File(this.getCodePath(), "Main.class");
                break;
            default:
                return true;
        }
        return file.exists();
    }
}
